package com.wbl.train.business.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wbl.train.business.service.DailyTrainSeatService;
import com.wbl.train.business.service.TrainStationService;
import com.wbl.train.common.business.dto.DailyTrainTicketDto;
import com.wbl.train.common.business.entity.DailyTrainEntity;
import com.wbl.train.common.business.entity.DailyTrainTicketEntity;
import com.wbl.train.business.mapper.DailyTrainTicketMapper;
import com.wbl.train.business.service.DailyTrainTicketService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wbl.train.common.business.entity.TrainStationEntity;
import com.wbl.train.common.business.enums.SeatTypeEnum;
import com.wbl.train.common.business.enums.TrainTypeEnum;
import com.wbl.train.common.exception.ApplicationException;
import com.wbl.train.common.page.PageVo;
import com.wbl.train.common.result.ResultCode;
import com.wbl.train.common.utils.SnowUtil;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

/**
 * 余票信息 服务实现类
 * @author 王博纶
 * @since 2024-02-15
 */
@AllArgsConstructor
@Slf4j
@Service
public class DailyTrainTicketServiceImpl extends ServiceImpl<DailyTrainTicketMapper, DailyTrainTicketEntity> implements DailyTrainTicketService {

    private final TrainStationService trainStationService;
    private final DailyTrainSeatService dailyTrainSeatService;

    // 将方法的返回值进行缓存
    @Cacheable(value = "DailyTrainTicketServiceImpl.queryTrainTicket")
    @Override
    public PageVo<DailyTrainTicketEntity> queryTrainTicket(DailyTrainTicketDto dailyTrainTicketDto) {
        // 获取分页所需要的参数
        int page = dailyTrainTicketDto.getPage();
        int limit = dailyTrainTicketDto.getLimit();

        LambdaQueryWrapper<DailyTrainTicketEntity> dailyTrainTicketWrapper = new LambdaQueryWrapper<DailyTrainTicketEntity>()
                .eq(ObjectUtil.isNotEmpty(dailyTrainTicketDto.getTrainCode()),DailyTrainTicketEntity::getTrainCode,dailyTrainTicketDto.getTrainCode())
                .eq(ObjectUtil.isNotNull(dailyTrainTicketDto.getDate()),DailyTrainTicketEntity::getDate,dailyTrainTicketDto.getDate())
                .eq(ObjectUtil.isNotNull(dailyTrainTicketDto.getStart()),DailyTrainTicketEntity::getStart,dailyTrainTicketDto.getStart())
                .eq(ObjectUtil.isNotNull(dailyTrainTicketDto.getEnd()),DailyTrainTicketEntity::getEnd,dailyTrainTicketDto.getEnd())
                .orderByDesc(DailyTrainTicketEntity::getDate)
                .orderByAsc(DailyTrainTicketEntity::getTrainCode);

        //创建分页对象
        IPage<DailyTrainTicketEntity> dailyTrainTicketPage = new Page<>(page,limit);
        //调用分页方法
        IPage<DailyTrainTicketEntity> dailyTrainTicketPageData = page(dailyTrainTicketPage,dailyTrainTicketWrapper);
        // 获取查询出来的数据集合
        List<DailyTrainTicketEntity> records = dailyTrainTicketPageData.getRecords();
        // 将数据塞到PageVo类中并返回出去
        return new PageVo<DailyTrainTicketEntity>()
                .setDataList(records)
                .setPageSize(limit).setCurrentPage(page)
                .setTotal((int) dailyTrainTicketPageData.getTotal())
                .setPages((int) dailyTrainTicketPageData.getPages());
    }

    @Override
    public boolean insertAndUpdateTrain(DailyTrainTicketEntity dailyTrainTicket) {
        // 获取当前的时间 --- 用于创建和修改的时候的时间填充
        DateTime now = DateTime.now();
        // 设置火车车站的修改时间
        dailyTrainTicket.setUpdateTime(now);
        // 判断Id是否为空
        if(ObjectUtil.isNotNull(dailyTrainTicket.getId())){
            // 根据id查询火车车站信息
            DailyTrainTicketEntity dailyTrainTicketEntity = this.getById(dailyTrainTicket.getId());
            // 当前id对应的无火车车站信息
            if(ObjectUtil.isNull(dailyTrainTicketEntity)){
                throw new ApplicationException(ResultCode.DATA_NOT_EXIST,"当前火车余票不存在");
            }
            // 更新火车车次信息
            return this.updateById(dailyTrainTicket);
        }
        // 保存之前的唯一键校验
        DailyTrainTicketEntity dailyTrainTicketEntity = getTrainTicketByUniqueKey(dailyTrainTicket.getDate(),
                                                                                dailyTrainTicket.getTrainCode(),
                                                                                dailyTrainTicket.getStart(),
                                                                                dailyTrainTicket.getEnd());
        // 查询出的trainEntity不为空，不允许新增
        if(ObjectUtil.isNotEmpty(dailyTrainTicketEntity)){
            throw new ApplicationException(ResultCode.DATA_EXIST,"当前火车余票已存在");
        }
        // 给新增的火车车站设置Id，创建时间和修改时间
        dailyTrainTicket.setId(SnowUtil.getSnowflakeNextId())
                .setCreateTime(now);
        // 新增火车车站
        return this.save(dailyTrainTicket);
    }

    /**
     * 根据唯一键查询 trainTicket 火车余票
     * @param date 当日日期
     * @param trainCode 火车车次编号
     * @param start 火车起始站
     * @param end 火车结束站
     * @return trainTicket 火车余票
     */
    public DailyTrainTicketEntity getTrainTicketByUniqueKey(LocalDate date, String trainCode,String start,String end) {
        // 通过trainCode和stationIndex进行火车车站信息联合主键，进行去重查询
        LambdaQueryWrapper<DailyTrainTicketEntity> wrapper = new LambdaQueryWrapper<DailyTrainTicketEntity>()
                .eq(DailyTrainTicketEntity::getDate,date)
                .eq(DailyTrainTicketEntity::getTrainCode, trainCode)
                .eq(DailyTrainTicketEntity::getStart,start)
                .eq(DailyTrainTicketEntity::getEnd,end);
        return this.getOne(wrapper);
    }


    @Transactional
    public void genTrainTicket(DailyTrainEntity dailyTrainEntity,LocalDate date, String trainCode, Date now){
        log.info("生成日期【{}】车次【{}】的余票信息开始",date,trainCode);
        // 初始化用于批量新增的集合
        ArrayList<DailyTrainTicketEntity> dailyTrainTicketList = new ArrayList<>();

        // 删除某日某车次的车站信息
        LambdaUpdateWrapper<DailyTrainTicketEntity> deleteWrapper = new LambdaUpdateWrapper<DailyTrainTicketEntity>()
                .eq(DailyTrainTicketEntity::getTrainCode, trainCode)
                .eq(DailyTrainTicketEntity::getDate, date);
        this.remove(deleteWrapper);

        // 查询途径的车站信息
        List<TrainStationEntity> trainStations = trainStationService.selectByTrainCode(trainCode);
        if(CollUtil.isEmpty(trainStations)){
            log.error("没有车站基础数据，生成改车次余票信息结束");
            throw new ApplicationException(ResultCode.DATA_NOT_EXIST,"没有车站基础数据，生成改车次余票信息结束");
        }

        // 获取当前火车车次的类型
        String trainType = dailyTrainEntity.getType();
        // 根据火车车次类型获取票价系数
        BigDecimal priceRate = EnumUtil.getFieldBy(TrainTypeEnum::getPriceRate, TrainTypeEnum::getCode, trainType);

        // 获取各种座位的余票数量 --- 座位类型对应的座位数量
        int ydz = dailyTrainSeatService.countSeat(date, trainCode, SeatTypeEnum.YDZ.getCode());
        int edz = dailyTrainSeatService.countSeat(date, trainCode, SeatTypeEnum.EDZ.getCode());
        int rw = dailyTrainSeatService.countSeat(date, trainCode, SeatTypeEnum.RW.getCode());
        int yw = dailyTrainSeatService.countSeat(date, trainCode, SeatTypeEnum.YW.getCode());

        // 初始化里程
        BigDecimal sumKm = BigDecimal.ZERO;

        for (int i = 0; i < trainStations.size(); i++) {
            // 获取出发站
            TrainStationEntity trainStationStart = trainStations.get(i);
            for (int j = i + 1; j < trainStations.size(); j++) {
                TrainStationEntity trainStationEnd = trainStations.get(j);
                // 对里程进行累加
                sumKm = sumKm.add(trainStationEnd.getKm());
                // 创建并添加到集合中用于批量新增
                DailyTrainTicketEntity dailyTrainTicketEntity = new DailyTrainTicketEntity()
                        .setId(SnowUtil.getSnowflakeNextId())
                        .setDate(date)
                        .setTrainCode(trainCode)
                        .setStart(trainStationStart.getName())
                        .setStartPinyin(trainStationStart.getNamePinyin())
                        .setStartTime(trainStationStart.getOutTime())
                        .setStartIndex(trainStationStart.getStationIndex())
                        .setEnd(trainStationEnd.getName())
                        .setEndPinyin(trainStationEnd.getNamePinyin())
                        .setEndTime(trainStationEnd.getInTime())
                        .setEndIndex(trainStationEnd.getStationIndex())
                        .setYdz(ydz)
                        // 票价 = 里程之和 * 座位单价 * 车次类型的系数
                        .setYdzPrice(sumKm.multiply(SeatTypeEnum.YDZ.getPrice()).multiply(priceRate).setScale(2, RoundingMode.HALF_UP))
                        .setEdz(edz)
                        .setEdzPrice(sumKm.multiply(SeatTypeEnum.EDZ.getPrice()).multiply(priceRate).setScale(2, RoundingMode.HALF_UP))
                        .setRw(rw)
                        .setRwPrice(sumKm.multiply(SeatTypeEnum.RW.getPrice()).multiply(priceRate).setScale(2, RoundingMode.HALF_UP))
                        .setYw(yw)
                        .setYwPrice(sumKm.multiply(SeatTypeEnum.YW.getPrice()).multiply(priceRate).setScale(2, RoundingMode.HALF_UP))
                        .setCreateTime(now)
                        .setUpdateTime(now);
                dailyTrainTicketList.add(dailyTrainTicketEntity);
            }
        }
        // 生成每日余票
        this.saveBatch(dailyTrainTicketList);
        log.info("生成日期【{}】车次【{}】的余票信息结束",date,trainCode);
    }

}
